/*
 * Copyright 2023-2025, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */
#ifndef _KERNEL_ARCH_LOONGARCH64_LOONGARCH_VM_TRANSLATION_MAP_H
#define _KERNEL_ARCH_LOONGARCH64_LOONGARCH_VM_TRANSLATION_MAP_H

#include <vm/VMTranslationMap.h>

// 龙芯LoongArch页表项结构定义
// 参考龙芯架构手册第5.3章

// 页表项标志位定义
const uint64 kPteValid = 1ULL << 0;        // V - 有效位
const uint64 kPteRead = 1ULL << 1;         // R - 读权限
const uint64 kPteWrite = 1ULL << 2;        // W - 写权限
const uint64 kPteExec = 1ULL << 3;         // X - 执行权限
const uint64 kPteUser = 1ULL << 4;         // U - 用户态访问权限
const uint64 kPteGlobal = 1ULL << 5;       // G - 全局页
const uint64 kPteAccessed = 1ULL << 6;     // A - 访问位
const uint64 kPteDirty = 1ULL << 7;        // D - 脏位
const uint64 kPteNoCache = 1ULL << 8;      // NC - 非缓存
const uint64 kPtePhysAddrMask = ~0x3ffULL; // 物理地址掩码

// 页表级别定义
const int kPageTableLevels = 4; // LoongArch使用4级页表

// 页表索引位定义
const int kPageTableIndexBits = 9;                             // 每级页表索引位数
const int kPageTableEntriesPerPage = 1 << kPageTableIndexBits; // 每页页表项数

// 虚拟地址位域定义
const int kVirtAddrPageOffsetBits = 12; // 页内偏移位数
const int kVirtAddrL0IndexBits = 9;     // 第0级页表索引位数
const int kVirtAddrL1IndexBits = 9;     // 第1级页表索引位数
const int kVirtAddrL2IndexBits = 9;     // 第2级页表索引位数
const int kVirtAddrL3IndexBits = 9;     // 第3级页表索引位数

// 虚拟地址位域偏移
const int kVirtAddrL0IndexShift = kVirtAddrPageOffsetBits;
const int kVirtAddrL1IndexShift = kVirtAddrL0IndexShift + kVirtAddrL0IndexBits;
const int kVirtAddrL2IndexShift = kVirtAddrL1IndexShift + kVirtAddrL1IndexBits;
const int kVirtAddrL3IndexShift = kVirtAddrL2IndexShift + kVirtAddrL2IndexBits;

// 虚拟地址位域掩码
const uint64 kVirtAddrL0IndexMask = ((1ULL << kVirtAddrL0IndexBits) - 1) << kVirtAddrL0IndexShift;
const uint64 kVirtAddrL1IndexMask = ((1ULL << kVirtAddrL1IndexBits) - 1) << kVirtAddrL1IndexShift;
const uint64 kVirtAddrL2IndexMask = ((1ULL << kVirtAddrL2IndexBits) - 1) << kVirtAddrL2IndexShift;
const uint64 kVirtAddrL3IndexMask = ((1ULL << kVirtAddrL3IndexBits) - 1) << kVirtAddrL3IndexShift;

// 页表项结构体
struct loongarch_pte
{
    uint64 val;

    inline bool IsValid() const { return (val & kPteValid) != 0; }
    inline bool IsReadable() const { return (val & kPteRead) != 0; }
    inline bool IsWritable() const { return (val & kPteWrite) != 0; }
    inline bool IsExecutable() const { return (val & kPteExec) != 0; }
    inline bool IsUserAccessible() const { return (val & kPteUser) != 0; }
    inline bool IsGlobal() const { return (val & kPteGlobal) != 0; }
    inline bool IsAccessed() const { return (val & kPteAccessed) != 0; }
    inline bool IsDirty() const { return (val & kPteDirty) != 0; }
    inline bool IsNoCache() const { return (val & kPteNoCache) != 0; }

    inline phys_addr_t PhysicalAddress() const { return val & kPtePhysAddrMask; }
};

// 从虚拟地址提取页表索引
inline uint32 VirtAddrPte(addr_t vaddr, int level)
{
    switch (level)
    {
    case 0:
        return (vaddr & kVirtAddrL0IndexMask) >> kVirtAddrL0IndexShift;
    case 1:
        return (vaddr & kVirtAddrL1IndexMask) >> kVirtAddrL1IndexShift;
    case 2:
        return (vaddr & kVirtAddrL2IndexMask) >> kVirtAddrL2IndexShift;
    case 3:
        return (vaddr & kVirtAddrL3IndexMask) >> kVirtAddrL3IndexShift;
    default:
        return 0;
    }
}

// 物理地址到虚拟地址的转换
inline addr_t VirtFromPhys(phys_addr_t paddr)
{
    // 使用龙芯手册第6.2节定义的地址空间映射
    // 内核物理内存映射区域从KERNEL_PMAP_BASE开始
    return KERNEL_PMAP_BASE + paddr;
}

// LoongArch VM转换映射类
class LoongArchVMTranslationMap : public VMTranslationMap
{
public:
    LoongArchVMTranslationMap(bool kernel, phys_addr_t pageTable);
    ~LoongArchVMTranslationMap();

    virtual bool Lock();
    virtual void Unlock();

    virtual addr_t MappedSize() const;
    virtual size_t MaxPagesNeededToMap(addr_t start, addr_t end) const;

    virtual status_t Map(addr_t virtualAddress, phys_addr_t physicalAddress,
                         uint32 attributes, uint32 memoryType, vm_page_reservation *reservation);
    virtual status_t Unmap(addr_t start, addr_t end);

    virtual status_t DebugMarkRangePresent(addr_t start, addr_t end, bool markPresent);

    virtual status_t UnmapPage(VMArea *area, addr_t address, bool updatePageQueue);
    virtual void UnmapPages(VMArea *area, addr_t base, size_t size, bool updatePageQueue);
    virtual void UnmapArea(VMArea *area, bool deletingAddressSpace, bool ignorePermissions);

    virtual status_t Query(addr_t virtualAddress, phys_addr_t *_physicalAddress,
                           uint32 *_flags);
    virtual status_t QueryInterrupt(addr_t virtualAddress, phys_addr_t *_physicalAddress,
                                    uint32 *_flags);

    virtual status_t Protect(addr_t base, addr_t top, uint32 attributes, uint32 memoryType);

    virtual status_t ClearFlags(addr_t virtualAddress, uint32 flags);

    virtual bool ClearAccessedAndModified(VMArea *area, addr_t address,
                                          bool unmapIfUnaccessed, bool &_modified);

    virtual void Flush();

    // LoongArch特定方法
    uint32 ASID() const { return fASID; }
    void ChangeASID(uint32 asid);

    // 硬件特性标志
    static uint32 fHwFeature;

private:
    phys_addr_t fPageTable;
    uint32 fASID;
    bool fIsKernel;
};

// LoongArch物理页映射器类
class LoongArchVMPhysicalPageMapper : public VMPhysicalPageMapper
{
public:
    LoongArchVMPhysicalPageMapper();
    virtual ~LoongArchVMPhysicalPageMapper();

    virtual status_t GetPage(phys_addr_t physicalAddress, addr_t *_virtualAddress,
                             void **_handle);
    virtual status_t PutPage(addr_t virtualAddress, void *handle);

    virtual status_t GetPageCurrentCPU(phys_addr_t physicalAddress,
                                       addr_t *_virtualAddress, void **_handle);
    virtual status_t PutPageCurrentCPU(addr_t virtualAddress, void *handle);

    virtual status_t GetPageDebug(phys_addr_t physicalAddress,
                                  addr_t *_virtualAddress, void **_handle);
    virtual status_t PutPageDebug(addr_t virtualAddress, void *handle);

    virtual status_t MemsetPhysical(phys_addr_t address, int value, phys_size_t length);
    virtual status_t MemcpyFromPhysical(void *to, phys_addr_t from, size_t length,
                                        bool user);
    virtual status_t MemcpyToPhysical(phys_addr_t to, const void *from, size_t length,
                                      bool user);
    virtual void MemcpyPhysicalPage(phys_addr_t to, phys_addr_t from);
};

#endif /* _KERNEL_ARCH_LOONGARCH64_LOONGARCH_VM_TRANSLATION_MAP_H */